package org.jvm.instruction.base;

import org.jvm.rtda.Object;
import org.jvm.rtda.*;
import org.jvm.rtda.heap.Klass;
import org.jvm.rtda.heap.classmember.Method;
import org.jvm.rtda.thread.Thread;
import org.jvm.rtda.thread.*;
import org.jvm.util.JavaCallUtil;

/**
 * @author 海燕
 * @date 2023/1/29
 */
public class InstructionUtil {

    /**
     * @param frame
     * @param offset offset实际上已经考虑到了读取操作数时程序计数器的位移量
     */
    static public void branch(Frame frame, int offset) {
        int pc = frame.getThread().getPc();
        pc += offset;
        frame.setNextPC(pc);
    }

    /**
     * 线程调用方法
     *
     * @param frame
     * @param method
     */
    public static void invokeMethod(Frame frame, Method method) {
        //获取当前线程
        Thread thread = frame.getThread();
        //生成新栈帧并压栈，对应被调用方法
        Frame newFrame = thread.newFrameAndPush(method);
        //传递方法参数，初始化新栈帧本地变量表
        int argSlotCount = method.getArgSlotCount();
        //从操作数栈中依次弹出slot，赋值给新栈帧的本地变量表
        for (int i = argSlotCount - 1; i >= 0; i--) {
            Slot slot = frame.getOperandStack().popSlot();
            newFrame.getLocalVars().setSlot(i, slot);
        }
    }

    /**
     * 类初始化，并递归初始化超类
     * 使用javaCall进行类初始化
     *
     * @param thread
     * @param klass
     */
    public static void initClass(Thread thread, Klass klass) {
        if (klass.isInitStarted()) {
            return;
        }
        //设置初始化标识，避免死循环
        klass.startedInit();
        //如果超类没有初始化，则先开始超类的初始化。
        if (!klass.isInterface() && klass.getSuperClass() != null && !klass.getSuperClass().isInitStarted()) {
            initClass(thread, klass.getSuperClass());
        }
        //获取类的初始化方法<clinit>，这个方法是()V的，调用javaCall进行初始化
        Method clinitMethod = klass.getClinitMethod();
        if (clinitMethod != null) {
            JavaCallUtil.javaCall(thread, clinitMethod, null);
        }
    }

    /**
     * 通过JavaCall方式调用类的初始化方法
     *
     * @param thread
     * @param klass
     */
    public static void initClassByJavaCall(Thread thread, Klass klass) {
        //设置初始化标识，避免死循环
        klass.startedInit();
        //如果超类没有初始化，则先开始超类的初始化。超类初始化方法的栈帧会再压栈顶，并先执行
        if (!klass.isInterface() && klass.getSuperClass() != null && !klass.getSuperClass().isInitStarted()) {
            initClassByJavaCall(thread, klass.getSuperClass());
        }
        //获取类的初始化方法<clinit>，这个方法是()V的，直接压线程栈。使得线程先执行类初始化方法
        Method clinitMethod = klass.getClinitMethod();
        if (clinitMethod != null) {
            JavaCallUtil.javaCall(thread, clinitMethod, null);
        }
    }

    /**
     * 创建多维数组
     *
     * @param start    counts的起始位
     * @param counts   每个维度的长度
     * @param arrClass 多维数组类
     * @return 多维数组引用
     */
    public static Object newMultDimensionalArray(int start, int[] counts, Klass arrClass) {
        //本维度数组长度
        int count = counts[0];
        if (count < 0) {
            throw new RuntimeException("array<0");
        }
        Object array = arrClass.newArray(count);
        //如果数组仅有一维，就不向下递归了。否则本维度数组的每个项里都应存在一个count-1维度的数组
        if (counts.length > start + 1) {
            for (int i = 0; i < count; i++) {
                array.refs()[i] = newMultDimensionalArray(start + 1, counts, arrClass.getComponentClass());
            }
        }
        return array;
    }
}
